using System;
using System.Collections.Generic;
using UnityEngine;

public partial class BundleTool
{
    public static string[] UniPath = new string[]
    {
        "Res/Shader",
        "Res/Conf",
    };

    [Serializable]
    public class AssetItem
    {
        public string Path;

        public string Bundle;

        //依赖项
        public List<string> Relies = new();

        //被依赖项
        public List<string> BeRelies = new();
    }

    [Serializable]
    public class BundleItem
    {
        public string Name;

        public string Hash;

        public int Offset;
        public int Position;
        public int Length;

        public List<string> Assets = new();
    }

    [Serializable]
    public class JsonObj
    {
        public List<AssetItem> Assets = new();
        public List<BundleItem> Bundles = new();
    }

    private static Dictionary<string, AssetItem> dicA = new();
    private static Dictionary<string, BundleItem> dicB = new();

    private static List<AssetItem> mCircularPath = new();

    private static void AddAsset(string path)
    {
        if (dicA.ContainsKey(path)) return;

        dicA[path] = new AssetItem
        {
            Path = path,
        };
    }

    private static void SetAssetRely(string path, string rely_pah)
    {
        if (dicA.ContainsKey(path) == false || dicA.ContainsKey(rely_pah) == false)
        {
            return;
        }

        var item1 = dicA[path];
        var item2 = dicA[rely_pah];
        if (item1.Relies.Contains(rely_pah) == false)
        {
            item1.Relies.Add(rely_pah);
        }

        if (item2.BeRelies.Contains(path) == false)
        {
            item2.BeRelies.Add(path);
        }
    }

    /// <summary>
    /// 检测资源的循环依赖
    ///
    /// 不允许存在循环依赖
    /// </summary>
    /// <returns></returns>
    private static void CheckCircularRely()
    {
        var result = false;

        mCircularPath.Clear();

        foreach (var item in dicA)
        {
            var asset = item.Value;
            mCircularPath.Add(asset);
            result = Traverse(asset);
            if (result)
            {
                break;
            }

            mCircularPath.RemoveAt(mCircularPath.Count - 1);
        }

        if (result)
        {
            for (int i = 0; i < mCircularPath.Count; i++)
            {
                var asset = mCircularPath[i];
                Debug.LogError(asset.Path);
            }

            throw new Exception($"Error!!! 以上资源存在循环依赖");
        }
    }

    private static bool Traverse(AssetItem asset)
    {
        var cnt = 0;

        for (int i = 0; i < mCircularPath.Count; i++)
        {
            if (mCircularPath[i] == asset)
            {
                cnt++;
            }
        }

        if (cnt > 1)
        {
            return true;
        }

        for (int i = 0; i < asset.Relies.Count; i++)
        {
            var path = asset.Relies[i];
            var re_asset = dicA[path];
            mCircularPath.Add(re_asset);
            Traverse(re_asset);
            mCircularPath.RemoveAt(mCircularPath.Count - 1);
        }

        return false;
    }


    private static string GetBundleName(AssetItem asset)
    {
        var index1 = asset.Path.LastIndexOf("/", StringComparison.Ordinal);
        var bundle_name = asset.Path[..index1];

        for (int i = 0; i < UniPath.Length; i++)
        {
            var index2 = bundle_name.IndexOf(UniPath[i], StringComparison.Ordinal);
            if (index2 >= 0)
            {
                bundle_name = UniPath[i];
                break;
            }
        }

        bundle_name = bundle_name.Replace("/", "_");
        return bundle_name;
    }

    private static bool IsUniPath(AssetItem asset)
    {
        for (int i = 0; i < UniPath.Length; i++)
        {
            var index = asset.Path.IndexOf(UniPath[i], StringComparison.Ordinal);
            if (index >= 0)
            {
                return true;
            }
        }

        return false;
    }

    private static void SetBundleName(AssetItem asset, string bundle_name)
    {
        bundle_name = bundle_name.ToLower();

        asset.Bundle = bundle_name;

        if (dicB.ContainsKey(bundle_name) == false)
        {
            dicB[bundle_name] = new BundleItem
            {
                Name = bundle_name
            };
        }

        var item = dicB[bundle_name];
        if (item.Assets.Contains(asset.Path) == false)
        {
            item.Assets.Add(asset.Path);
        }
    }

    private static void SetBundleName(AssetItem asset)
    {
        if (string.IsNullOrEmpty(asset.Bundle) == false)
        {
            return;
        }

        if (IsUniPath(asset) || asset.BeRelies.Count == 0)
        {
            SetBundleName(asset, GetBundleName(asset));
            return;
        }

        bool isSame = true;
        string bundle_name = string.Empty;
        for (int i = 0; i < asset.BeRelies.Count; i++)
        {
            var name = asset.BeRelies[i];
            var asset1 = dicA[name];
            if (string.IsNullOrEmpty(asset1.Bundle))
            {
                SetBundleName(asset1);
            }

            if (bundle_name == string.Empty)
            {
                bundle_name = asset1.Bundle;
            }
            else if (bundle_name != asset1.Bundle)
            {
                isSame = false;
                break;
            }
        }

        SetBundleName(asset, isSame ? bundle_name : GetBundleName(asset));
    }

    private static string GetResListJson()
    {
        var obj = new JsonObj();

        foreach (var pair in dicA)
        {
            obj.Assets.Add(pair.Value);
        }

        foreach (var pair in dicB)
        {
            obj.Bundles.Add(pair.Value);
        }

        var json = JsonUtility.ToJson(obj);
        Debug.Log(json);
        return json;
    }
}
